#ifndef _PCIe_ROOT_COMPLEX_H
#define _PCIe_ROOT_COMPLEX_H

#include "CPU_if.h"
#include "PCIe_port.h"
#include "PCIe_Endpoint.h"
#include "SDRAM_DDR_device_if.h"
#include "SDRAM_DDR_DP_device_if.h"

//#define DEBUG_RC
//#define DEBUG_RC_SDRAM_CTRL
//#define DEBUG_RC_SSD_CTRL

const unsigned	MAX_BUFFER_RC					= 8;
const unsigned	DMI_NUM_OF_VIRTUAL_CHANNEL_RC	= 1;
const unsigned	SDRAM_EXTERNAL_BANK_NUM			= 1;

class PCIe_Root_Complex
: public sc_module
, public CPU_if
, public PCIe_if
, public PCIe_Endpoint
{
public:
	sc_in_clk		CPU_CLK;
	sc_in_clk		DRAM_CLK;
	sc_in_clk		DMI_CLK;
    sc_in<bool>		RSTn;
	
	sc_port<SDRAM_DDR_device_if, SDRAM_EXTERNAL_BANK_NUM> DDR_SDRAM_port;
	sc_port<SDRAM_DDR_DP_device_if, SDRAM_EXTERNAL_BANK_NUM> DPDRAM_port;
	sc_port<SDRAM_DDR_device_if, 1> SSD_port;
	
//	PCIe_port		graphic_port;
//	PCIe_lane_t		graphic_lane;
	PCIe_port		DMI_port;
	PCIe_lane_t		DMI_lane;

	virtual const bool
	CPU_init_transaction(bool is_write_,
						 CPU_addr_t addr_,
						 unsigned data_length_);

	virtual const bool
	CPU_end_transaction();

	virtual const bool
	CPU_is_ready() const;

	virtual const bool
	CPU_is_done() const;

	virtual const CPU_data_t
	CPU_read_data();

	virtual const bool
	CPU_write_data(CPU_data_t data_);

	virtual const bool
	CPU_check_interrupt(unsigned interrupt_type_);
	
	virtual void
	PL_send_symbol(PCIe_id_t		id_,
				   PCIe_symbol_t	symbol_);

	virtual const bool
	DLL_send_sequence_number_byte(PCIe_id_t	id_,
								  byte		seq_num_);

	virtual const bool
	DLL_send_type_byte(PCIe_id_t	id_,
					   byte			type_byte_);

	virtual const bool
	DLL_send_data_byte(PCIe_id_t	id_,
					   byte			data_byte_);

	virtual const bool
	DLL_send_CRC_byte(PCIe_id_t	id_,
					  byte		CRC_byte_);

	virtual const bool
	DLL_send_LCRC_byte(PCIe_id_t	id_,
					   byte			LCRC_byte_);

	virtual void
	TL_send_header_byte(PCIe_id_t	id_,
						byte		header_byte_);

	virtual void
	TL_send_data_byte(PCIe_id_t	id_,
					  byte		data_byte_);

	virtual void
	TL_send_ECRC_byte(PCIe_id_t	id_,
					  byte		ECRC_byte_);

	SC_HAS_PROCESS(PCIe_Root_Complex);

	PCIe_Root_Complex(sc_module_name		name_,
					  PCIe_id_t				id_,
					  PCIe_id_t				North_DMA_id_,
					  unsigned				North_Main_DMA_PRD_start_addr_,
					  unsigned				North_DMA_cmd_reg_start_addr_,
					  unsigned				North_Main_DMA_check_reg_start_addr_,
					  PCIe_address_space	addr_DRAM_mem_1_,
					  PCIe_address_space	addr_DRAM_io_,
					  PCIe_address_space	addr_DRAM_conf_,
					  PCIe_address_space	addr_DMI_mem_1_,
					  PCIe_address_space	addr_DMI_io_,
					  PCIe_address_space	addr_DMI_conf_,
					  PCIe_address_space	addr_DRAM_mem_2_,
					  PCIe_address_space	addr_DMI_mem_2_,
					  unsigned				System_type_,
					  unsigned				DRAM_rank_index_,
					  unsigned				DRAM_bank_index_,
					  unsigned				DRAM_row_index_,
					  unsigned				DRAM_col_index_);

	~PCIe_Root_Complex();

protected:
	unsigned			System_type;

	bool				CPU_is_write;
	CPU_addr_t			CPU_address;
	CPU_data_t			CPU_data;
	CPU_data_t*			CPU_data_buffer;
	unsigned			CPU_data_length;
	unsigned			CPU_data_cnt;
	bool				CPU_data_ready;
	bool				CPU_write_done;
	bool				CPU_active;
	sc_time				CPU_active_time;
	bool				CPU_write_reguest_completed;
	sc_time				CPU_write_reguest_completed_time;

	PCIe_id_t			id;
	PCIe_id_t			North_DMA_id;

	PCIe_address_map	system_address_map;
	PCIe_address		North_Main_DMA_PRD_start_addr;
	PCIe_address		North_Main_DMA_PRD_start_addr_init;
	PCIe_address		North_DMA_cmd_reg_start_addr;
	PCIe_address		North_Main_DMA_check_reg_start_addr;

	PCIe_state_t		DMI_current_state;

	bool				DMI_is_STP;
	bool				DMI_is_NAK;

	bool				DMI_stop;

	unsigned short		DMI_sequence_number;
	unsigned short		DMI_NAK_sequence_number;
	unsigned short		DMI_NAK_free_sequence_number;

	PCIe_TLP			CPU_buffer_TLP_down[MAX_BUFFER_RC];
	unsigned			CPU_buffer_TLP_down_in;
	unsigned			CPU_buffer_TLP_down_out;
	sc_time				CPU_buffer_TLP_down_time;

	PCIe_TLP			CPU_buffer_TLP_up[MAX_BUFFER_RC];
	unsigned			CPU_buffer_TLP_up_in;
	unsigned			CPU_buffer_TLP_up_out;
	sc_time				CPU_buffer_TLP_up_time;

	PCIe_DLL_TLP		DMI_buffer_DLL_TLP_down[MAX_BUFFER_RC];
	unsigned			DMI_buffer_DLL_TLP_down_in;
	unsigned			DMI_buffer_DLL_TLP_down_out;
	unsigned			DMI_buffer_DLL_TLP_down_ack_nak;
	sc_time				DMI_buffer_DLL_TLP_down_time;

	PCIe_DLL_TLP		DMI_buffer_DLL_TLP_up[MAX_BUFFER_RC];
	unsigned			DMI_buffer_DLL_TLP_up_in;
	unsigned			DMI_buffer_DLL_TLP_up_out;
	sc_time				DMI_buffer_DLL_TLP_up_time;

	PCIe_TLP			DRAM_buffer_TLP_down[MAX_BUFFER_RC];
	unsigned			DRAM_buffer_TLP_down_in;
	unsigned			DRAM_buffer_TLP_down_out;
	sc_time				DRAM_buffer_TLP_down_time;

	PCIe_TLP			DRAM_buffer_TLP_up[MAX_BUFFER_RC];
	unsigned			DRAM_buffer_TLP_up_in;
	unsigned			DRAM_buffer_TLP_up_out;
	sc_time				DRAM_buffer_TLP_up_time;

	PCIe_TLP			DMI_buffer_TLP_down[MAX_BUFFER_RC];
	unsigned			DMI_buffer_TLP_down_in;
	unsigned			DMI_buffer_TLP_down_out;
	sc_time				DMI_buffer_TLP_down_time;

	PCIe_TLP			DMI_buffer_TLP_up[MAX_BUFFER_RC];
	unsigned			DMI_buffer_TLP_up_in;
	unsigned			DMI_buffer_TLP_up_out;
	sc_time				DMI_buffer_TLP_up_time;

	unsigned			DMI_DLL_TLP_seq_num_cnt;
	unsigned			DMI_TLP_header_cnt;
	unsigned			DMI_TLP_data_cnt;
	unsigned			DMI_TLP_ECRC_cnt;
	unsigned			DMI_DLL_TLP_LCRC_cnt;

	PCIe_DLLP			DMI_buffer_DLLP_down[MAX_BUFFER_RC];
	unsigned			DMI_buffer_DLLP_down_in;
	unsigned			DMI_buffer_DLLP_down_out;
	sc_time				DMI_buffer_DLLP_down_time;

	PCIe_DLLP			DMI_buffer_DLLP_up[MAX_BUFFER_RC];
	unsigned			DMI_buffer_DLLP_up_in;
	unsigned			DMI_buffer_DLLP_up_out;
	sc_time				DMI_buffer_DLLP_up_time;

	unsigned			DMI_DLLP_data_cnt;
	unsigned			DMI_DLLP_CRC_cnt;

	unsigned			DMI_granted_packet_type;

	unsigned			DRAM_rank_index;
	unsigned			DRAM_bank_index;
	unsigned			DRAM_row_index;
	unsigned			DRAM_col_index;

	unsigned			DRAM_refresh_cnt;
	bool				DRAM_refresh_enable;
	sc_time				DRAM_refresh_enable_time;

	unsigned*			North_Main_DMA_buffer;
	unsigned			North_Main_DMA_command;
	unsigned			North_Main_DMA_sector_cnt;
	unsigned			North_Main_DMA_LBA;
	unsigned			North_Main_DMA_LBA_current;
	unsigned			North_Main_DMA_PRD_length;

	bool				North_Main_DMA_enable;
	sc_time				North_Main_DMA_enable_time;
	bool				North_Main_DMA_burst_change;
	bool				North_Main_DMA_PRD_fetch_commanded;
	bool				North_Main_DMA_PRD_fetched;
	sc_time				North_Main_DMA_PRD_fetched_time;
	bool				North_Main_DMA_requested_1st;
	bool				North_Main_DMA_requested_2nd;
	bool				North_Main_DMA_buffered;
	sc_time				North_Main_DMA_buffered_time;
	bool				North_Main_DMA_write_check;

	unsigned			North_SSD_DMA_command;
	unsigned			North_SSD_DMA_sector_cnt;
	unsigned			North_SSD_DMA_LBA;
	unsigned			North_SSD_DMA_PRD_length;

	unsigned			North_Main_DMA_base_addr;
	unsigned			North_Main_DMA_base_cnt;

	unsigned			North_Main_DMA_end;
	unsigned			North_SSD_DMA_end;
	bool				North_SSD_DMA_end_check;
	unsigned			South_Main_DMA_end;
	unsigned			South_SSD_DMA_end;

	bool				North_SSD_DMA_stop;

	unsigned			PRD_current;

	const bool
	arbitrate_port(PCIe_id_t	id_,
				   unsigned		packet_type_);

	const unsigned
	decode_address(PCIe_address	addr_) const;

	const char*
	SDRAM_get_state_name(SDRAM_state_t	state_) const;

	void
	CPU_make_action();

	void
	CPU_switch_action();

	void
	DRAM_switch_action();

	void
	DMI_make_action();

	void
	DMI_switch_action();

	void
	DMI_TLP_send_action();

	void
	DMI_ack_nak_action();

	void
	DMI_DLLP_send_action();

	void
	DRAM_refresh_count_action();

	void
	DRAM_timing_control_action();

	void
	SSD_timing_control_action();

	void
	North_Main_DMA_action();
};

#endif